P&P笔记(X1) Exception Handle (二)

这一篇主要整理try catch的一些使用细节.

try catch finally 和 return

return在try catch finally中的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
可以这样
public bool SomeFunction()
{
try
{
//somecode
return true;
}
catch(Exception ex)
{
MessageBox.Show(ex.message);
return false;
}
}

或者
public bool SomeFunction()
{
bool success = true;
try
{
//somecode
}
catch(Exception ex)
{
MessageBox.Show(ex.message);
success = false;
}

return success;
}

finally中的逻辑会在return之前执行. finally中不能含有return.

using关键字

对于那些封装了非托管资源的类, 建议使用using关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using (Font font1 = new Font("Arial", 10.0f)) 
{
byte charset = font1.GdiCharSet;
}

等价于
{
Font font1 = new Font("Arial", 10.0f);
try
{
byte charset = font1.GdiCharSet;
}
finally
{
if (font1 != null)
((IDisposable)font1).Dispose();
}
}

using没有catch语句, 所以使用下列方式

1
2
3
4
5
6
7
8
9
10
11
try
{
using (var myObject = new MyClass())
{
// something here...
}
}
catch(Exception ex)
{
// Handle exception
}

当然不用using也可以, 例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static void Deserialize() 
{
// Declare the hashtable reference.
Hashtable addresses = null;

// Open the file containing the data that you want to deserialize.
FileStream fs = new FileStream("DataFile.dat", FileMode.Open);
try
{
BinaryFormatter formatter = new BinaryFormatter();

// Deserialize the hashtable from the file and
// assign the reference to the local variable.
addresses = (Hashtable) formatter.Deserialize(fs);
}
catch (SerializationException e)
{
Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
throw;
}
finally
{
fs.Close();
}

// To prove that the table deserialized correctly,
// display the key/value pairs.
foreach (DictionaryEntry de in addresses)
{
Console.WriteLine("{0} lives at {1}.", de.Key, de.Value);
}
}

多线程中的异常处理

Thread中的异常处理

http://www.albahari.com/threading/#_Exception_Handling 中有如下示例程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void Main()
{
try
{
new Thread (Go).Start();
}
catch (Exception ex)
{
// We'll never get here!
Console.WriteLine ("Exception!");
}
}

static void Go() { throw null; } // Throws a NullReferenceException

这种情况, NullReferenceException是不会进入catch语句块的. 正确的做法是在方法Go内部进行try/catch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void Main()
{
new Thread (Go).Start();
}

static void Go()
{
try
{
// ...
throw null; // The NullReferenceException will get caught below
// ...
}
catch (Exception ex)
{
// Typically log the exception, and/or signal another thread
// that we've come unstuck
// ...
}
}

为了防止应用程序崩溃, 链接中给出的建议是在所有线程的入口方法都加上try/catch

You need an exception handler on all thread entry methods in production applications — just as you do (usually at a higher level, in the execution stack) on your main thread. An unhandled exception causes the whole application to shut down. With an ugly dialog!

Task中的异常处理

Task中有两种处理方式, 第一种和前面Thread的处理方式一样, 例如:

1
Task.Run (() => { try { ... } catch (Exception ex) { ... } });

另一种是在Run的外面捕获, 例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Start a Task that throws a NullReferenceException:
Task task = Task.Run (() => { throw null; });
try
{
task.Wait();
}
catch (AggregateException aex)
{
if (aex.InnerException is NullReferenceException)
Console.WriteLine ("Null!");
else
throw;
}

与Thread不一样的是, Task能够propagate exception, C# in a nutshell中提到

Unlike with threads, tasks conveniently propagate exceptions. So, if the code in your task throws an unhandled exception (in other words, if your task faults), that exception is automatically rethrown to whoever calls Wait()—or accesses the Result property of a Task

需要注意的是task.Wait()和访问task.Result都是block Thread的. 使用AggregateException是为了和task parallel programming兼容

对于task中没有捕获的异常(unobserved exception), 还可以使用TaskScheduler.UnobservedTaskException事件进行监听, 例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//https://code.msdn.microsoft.com/windowsdesktop/Handling-Unhandled-47492d0b
private void TaskException_Click(object sender, RoutedEventArgs e)
{
Task t = Task.Factory.StartNew(() =>
{
throw new Exception("Task Exception!!", MyInnerException);
});

((IAsyncResult)t).AsyncWaitHandle.WaitOne();
t = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}

1
2
3
4
5
6
7
8
9
10
11
public App()
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}

void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
MessageBox.Show("4. TaskScheduler_UnobservedTaskException");
log.ProcessError(e.Exception);
e.SetObserved();
}

async中的异常处理

async中异常处理也很简单, 示例摘自Async in C# 5.0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async Task Catcher()
{
try
{
await Thrower();
}
catch (AlexsException)
{
// Execution will reach here
}
}

async Task Thrower()
{
await Task.Delay(100);
throw new AlexsException();
}